All files / src/app/api/support/tickets/[id]/survey route.ts

0% Statements 0/118
100% Branches 0/0
0% Functions 0/1
0% Lines 0/118

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119                                                                                                                                                                                                                                             
export const dynamic = "force-dynamic";

/**
 * Ticket Survey API
 * POST /api/support/tickets/[id]/survey - Submit satisfaction survey
 */

import { NextRequest, NextResponse } from "next/server";
import { Session } from "next-auth";
import { prisma } from "@/lib/prisma";
import { SubmitSurveySchema } from "@/lib/validation/support-schemas";
import { TicketStatus } from "@/types/support";
import { logger } from "@/lib/logging";
import {
  withErrorHandling,
  withAuth,
  createdResponse,
  ApiError } from "@/lib/api";
import { RouteContext } from "@/lib/api/middleware";

/**
 * POST /api/support/tickets/[id]/survey
 * Submit satisfaction survey for a resolved/closed ticket
 */
async function handlePost(
  request: NextRequest,
  context: RouteContext | undefined,
  session: Session
): Promise<NextResponse> {
  if (!context?.params) {
    throw ApiError.invalidId("ticket");
  }

  const { id } = await context.params;
  const userId = Number(session.user.id);

  // Verify ticket exists and belongs to user
  const ticket = await prisma.supportTicket.findUnique({
    where: { id },
    include: {
      survey: true,
      assignedTo: {
        select: { id: true }}}});

  if (!ticket) {
    throw ApiError.notFound("Ticket", id);
  }

  if (ticket.userId !== userId) {
    throw ApiError.forbidden("Access denied");
  }

  // Check if ticket is resolved or closed
  if (
    ticket.status !== TicketStatus.RESOLVED &&
    ticket.status !== TicketStatus.CLOSED
  ) {
    throw ApiError.validation("Survey can only be submitted for resolved tickets");
  }

  // Check if survey already exists
  if (ticket.survey) {
    throw ApiError.validation("Survey already submitted for this ticket");
  }

  // Validate input
  const body = await request.json();
  const validationResult = SubmitSurveySchema.safeParse(body);
  if (!validationResult.success) {
    throw ApiError.validation(
      "Validation failed",
      validationResult.error.flatten().fieldErrors
    );
  }

  const data = validationResult.data;

  // Create the survey
  const survey = await prisma.ticketSurvey.create({
    data: {
      ticketId: id,
      rating: data.rating,
      feedback: data.feedback || null,
      wasHelpful: data.wasHelpful ?? null,
      wouldRecommend: data.wouldRecommend ?? null}});

  // Update agent's average rating if assigned
  if (ticket.assignedToId) {
    // Get all surveys for this agent
    const agentSurveys = await prisma.ticketSurvey.findMany({
      where: {
        ticket: {
          assignedToId: ticket.assignedToId}},
      select: { rating: true }});

    const avgRating =
      agentSurveys.reduce((sum, s) => sum + s.rating, 0) / agentSurveys.length;

    await prisma.supportAgentProfile.upsert({
      where: { userId: ticket.assignedToId },
      update: { customerRating: avgRating },
      create: {
        userId: ticket.assignedToId,
        customerRating: avgRating}});
  }

  logger.info("Ticket survey submitted", {
    category: "API",
    ticketId: id,
    rating: data.rating,
    userId});

  return createdResponse({
    ...survey,
    message: "Thank you for your feedback!"});
}

export const POST = withErrorHandling(withAuth(handlePost));